/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- *//* vim: set ts=8 sts=2 et sw=2 tw=80: *//* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#include"nsCOMPtr.h"#include"nsXMLContentSink.h"#include"nsIParser.h"#include"nsIDocument.h"#include"nsIDOMDocument.h"#include"nsIDOMDocumentType.h"#include"nsIContent.h"#include"nsIURI.h"#include"nsNetUtil.h"#include"nsIDocShell.h"#include"nsIStyleSheetLinkingElement.h"#include"nsIDOMComment.h"#include"nsIDOMCDATASection.h"#include"DocumentType.h"#include"nsHTMLParts.h"#include"nsCRT.h"#include"mozilla/StyleSheetInlines.h"#include"mozilla/css/Loader.h"#include"nsGkAtoms.h"#include"nsContentUtils.h"#include"nsIScriptContext.h"#include"nsNameSpaceManager.h"#include"nsIServiceManager.h"#include"nsIScriptSecurityManager.h"#include"nsIContentViewer.h"#include"prtime.h"#include"mozilla/Logging.h"#include"nsRect.h"#include"nsIWebNavigation.h"#include"nsIScriptElement.h"#include"nsStyleLinkElement.h"#include"nsReadableUtils.h"#include"nsUnicharUtils.h"#include"nsICookieService.h"#include"nsIPrompt.h"#include"nsIChannel.h"#include"nsIPrincipal.h"#include"nsXMLPrettyPrinter.h"#include"nsNodeInfoManager.h"#include"nsContentCreatorFunctions.h"#include"nsIContentPolicy.h"#include"nsContentPolicyUtils.h"#include"nsError.h"#include"nsIDOMProcessingInstruction.h"#include"nsNodeUtils.h"#include"nsIScriptGlobalObject.h"#include"nsIHTMLDocument.h"#include"mozAutoDocUpdate.h"#include"nsMimeTypes.h"#include"nsHtml5SVGLoadDispatcher.h"#include"nsTextNode.h"#include"mozilla/dom/CDATASection.h"#include"mozilla/dom/Comment.h"#include"mozilla/dom/Element.h"#include"mozilla/dom/HTMLTemplateElement.h"#include"mozilla/dom/ProcessingInstruction.h"#include"mozilla/dom/ScriptLoader.h"usingnamespacemozilla;usingnamespacemozilla::dom;// XXX Open Issues:// 1) what's not allowed - We need to figure out which HTML tags// (prefixed with a HTML namespace qualifier) are explicitly not// allowed (if any).// 2) factoring code with nsHTMLContentSink - There's some amount of// common code between this and the HTML content sink. This will// increase as we support more and more HTML elements. How can code// from the code be factored?nsresultNS_NewXMLContentSink(nsIXMLContentSink**aResult,nsIDocument*aDoc,nsIURI*aURI,nsISupports*aContainer,nsIChannel*aChannel){NS_PRECONDITION(nullptr!=aResult,"null ptr");if(nullptr==aResult){returnNS_ERROR_NULL_POINTER;}RefPtr<nsXMLContentSink>it=newnsXMLContentSink();nsresultrv=it->Init(aDoc,aURI,aContainer,aChannel);NS_ENSURE_SUCCESS(rv,rv);it.forget(aResult);returnNS_OK;}nsXMLContentSink::nsXMLContentSink():mTextLength(0),mNotifyLevel(0),mPrettyPrintXML(true),mPrettyPrintHasSpecialRoot(0),mPrettyPrintHasFactoredElements(0),mPrettyPrinting(0),mPreventScriptExecution(0){PodArrayZero(mText);}nsXMLContentSink::~nsXMLContentSink(){}nsresultnsXMLContentSink::Init(nsIDocument*aDoc,nsIURI*aURI,nsISupports*aContainer,nsIChannel*aChannel){nsresultrv=nsContentSink::Init(aDoc,aURI,aContainer,aChannel);NS_ENSURE_SUCCESS(rv,rv);aDoc->AddObserver(this);mIsDocumentObserver=true;if(!mDocShell){mPrettyPrintXML=false;}mState=eXMLContentSinkState_InProlog;mDocElement=nullptr;returnNS_OK;}NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsXMLContentSink)NS_INTERFACE_MAP_ENTRY(nsIContentSink)NS_INTERFACE_MAP_ENTRY(nsIXMLContentSink)NS_INTERFACE_MAP_ENTRY(nsIExpatSink)NS_INTERFACE_MAP_ENTRY(nsITransformObserver)NS_INTERFACE_MAP_END_INHERITING(nsContentSink)NS_IMPL_ADDREF_INHERITED(nsXMLContentSink,nsContentSink)NS_IMPL_RELEASE_INHERITED(nsXMLContentSink,nsContentSink)NS_IMPL_CYCLE_COLLECTION_CLASS(nsXMLContentSink)NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXMLContentSink,nsContentSink)NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCurrentHead)NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocElement)for(uint32_ti=0,count=tmp->mContentStack.Length();i<count;i++){constStackNode&node=tmp->mContentStack.ElementAt(i);cb.NoteXPCOMChild(node.mContent);}NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END// nsIContentSinkNS_IMETHODIMPnsXMLContentSink::WillParse(void){returnWillParseImpl();}NS_IMETHODIMPnsXMLContentSink::WillBuildModel(nsDTDModeaDTDMode){WillBuildModelImpl();// Notify document that the load is beginningmDocument->BeginLoad();// Check for correct load-command for maybe prettyprintingif(mPrettyPrintXML){nsAutoCStringcommand;GetParser()->GetCommand(command);if(!command.EqualsLiteral("view")){mPrettyPrintXML=false;}}returnNS_OK;}boolnsXMLContentSink::CanStillPrettyPrint(){returnmPrettyPrintXML&&(!mPrettyPrintHasFactoredElements||mPrettyPrintHasSpecialRoot);}nsresultnsXMLContentSink::MaybePrettyPrint(){if(!CanStillPrettyPrint()){mPrettyPrintXML=false;returnNS_OK;}// stop observing in order to avoid crashing when replacing contentmDocument->RemoveObserver(this);mIsDocumentObserver=false;// Reenable the CSSLoader so that the prettyprinting stylesheets can loadif(mCSSLoader){mCSSLoader->SetEnabled(true);}RefPtr<nsXMLPrettyPrinter>printer;nsresultrv=NS_NewXMLPrettyPrinter(getter_AddRefs(printer));NS_ENSURE_SUCCESS(rv,rv);boolisPrettyPrinting;rv=printer->PrettyPrint(mDocument,&isPrettyPrinting);NS_ENSURE_SUCCESS(rv,rv);mPrettyPrinting=isPrettyPrinting;returnNS_OK;}staticvoidCheckXSLTParamPI(nsIDOMProcessingInstruction*aPi,nsIDocumentTransformer*aProcessor,nsIDocument*aDocument){nsAutoStringtarget,data;aPi->GetTarget(target);// Check for namespace declarationsif(target.EqualsLiteral("xslt-param-namespace")){aPi->GetData(data);nsAutoStringprefix,namespaceAttr;nsContentUtils::GetPseudoAttributeValue(data,nsGkAtoms::prefix,prefix);if(!prefix.IsEmpty()&&nsContentUtils::GetPseudoAttributeValue(data,nsGkAtoms::_namespace,namespaceAttr)){aProcessor->AddXSLTParamNamespace(prefix,namespaceAttr);}}// Check for actual parameterselseif(target.EqualsLiteral("xslt-param")){aPi->GetData(data);nsAutoStringname,namespaceAttr,select,value;nsContentUtils::GetPseudoAttributeValue(data,nsGkAtoms::name,name);nsContentUtils::GetPseudoAttributeValue(data,nsGkAtoms::_namespace,namespaceAttr);if(!nsContentUtils::GetPseudoAttributeValue(data,nsGkAtoms::select,select)){select.SetIsVoid(true);}if(!nsContentUtils::GetPseudoAttributeValue(data,nsGkAtoms::value,value)){value.SetIsVoid(true);}if(!name.IsEmpty()){nsCOMPtr<nsIDOMNode>doc=do_QueryInterface(aDocument);aProcessor->AddXSLTParam(name,namespaceAttr,select,value,doc);}}}NS_IMETHODIMPnsXMLContentSink::DidBuildModel(boolaTerminated){if(!mParser){// If mParser is null, this parse has already been terminated and must// not been terminated again. However, nsDocument may still think that// the parse has not been terminated and call back into here in the case// where the XML parser has finished but the XSLT transform associated// with the document has not.returnNS_OK;}DidBuildModelImpl(aTerminated);if(mXSLTProcessor){// stop observing in order to avoid crashing when replacing contentmDocument->RemoveObserver(this);mIsDocumentObserver=false;// Check for xslt-param and xslt-param-namespace PIsfor(nsIContent*child=mDocument->GetFirstChild();child;child=child->GetNextSibling()){if(child->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)){nsCOMPtr<nsIDOMProcessingInstruction>pi=do_QueryInterface(child);CheckXSLTParamPI(pi,mXSLTProcessor,mDocument);}elseif(child->IsElement()){// Only honor PIs in the prologbreak;}}nsCOMPtr<nsIDOMDocument>currentDOMDoc(do_QueryInterface(mDocument));mXSLTProcessor->SetSourceContentModel(currentDOMDoc);// Since the processor now holds a reference to us we drop our reference// to it to avoid owning cyclesmXSLTProcessor=nullptr;}else{// Kick off layout for non-XSLT transformed documents.// Check if we want to prettyprintMaybePrettyPrint();boolstartLayout=true;if(mPrettyPrinting){NS_ASSERTION(!mPendingSheetCount,"Shouldn't have pending sheets here!");// We're pretty-printing now. See whether we should wait up on// stylesheet loadsif(mDocument->CSSLoader()->HasPendingLoads()&&NS_SUCCEEDED(mDocument->CSSLoader()->AddObserver(this))){// wait for those sheets to loadstartLayout=false;}}if(startLayout){StartLayout(false);ScrollToRef();}mDocument->RemoveObserver(this);mIsDocumentObserver=false;mDocument->EndLoad();}DropParserAndPerfHint();returnNS_OK;}NS_IMETHODIMPnsXMLContentSink::OnDocumentCreated(nsIDocument*aResultDocument){NS_ENSURE_ARG(aResultDocument);nsCOMPtr<nsIHTMLDocument>htmlDoc=do_QueryInterface(aResultDocument);if(htmlDoc){htmlDoc->SetDocWriteDisabled(true);}nsCOMPtr<nsIContentViewer>contentViewer;mDocShell->GetContentViewer(getter_AddRefs(contentViewer));if(contentViewer){returncontentViewer->SetDocumentInternal(aResultDocument,true);}returnNS_OK;}NS_IMETHODIMPnsXMLContentSink::OnTransformDone(nsresultaResult,nsIDocument*aResultDocument){NS_ASSERTION(NS_FAILED(aResult)||aResultDocument,"Don't notify about transform success without a document.");nsCOMPtr<nsIDOMDocument>domDoc=do_QueryInterface(aResultDocument);nsCOMPtr<nsIContentViewer>contentViewer;mDocShell->GetContentViewer(getter_AddRefs(contentViewer));if(NS_FAILED(aResult)&&contentViewer){// Transform failed.if(domDoc){aResultDocument->SetMayStartLayout(false);// We have an error document.contentViewer->SetDOMDocument(domDoc);}else{// We don't have an error document, display the// untransformed source document.nsCOMPtr<nsIDOMDocument>document=do_QueryInterface(mDocument);contentViewer->SetDOMDocument(document);}}nsCOMPtr<nsIDocument>originalDocument=mDocument;if(NS_SUCCEEDED(aResult)||aResultDocument){// Transform succeeded or it failed and we have an error// document to display.mDocument=aResultDocument;nsCOMPtr<nsIHTMLDocument>htmlDoc=do_QueryInterface(mDocument);if(htmlDoc){htmlDoc->SetDocWriteDisabled(false);}}// Notify document observers that all the content has been stuck// into the document.// XXX do we need to notify for things like PIs? Or just the// documentElement?nsIContent*rootElement=mDocument->GetRootElement();if(rootElement){NS_ASSERTION(mDocument->IndexOf(rootElement)!=-1,"rootElement not in doc?");mDocument->BeginUpdate(UPDATE_CONTENT_MODEL);nsNodeUtils::ContentInserted(mDocument,rootElement,mDocument->IndexOf(rootElement));mDocument->EndUpdate(UPDATE_CONTENT_MODEL);}// Start the layout processStartLayout(false);ScrollToRef();originalDocument->EndLoad();returnNS_OK;}NS_IMETHODIMPnsXMLContentSink::StyleSheetLoaded(StyleSheet*aSheet,boolaWasAlternate,nsresultaStatus){if(!mPrettyPrinting){returnnsContentSink::StyleSheetLoaded(aSheet,aWasAlternate,aStatus);}if(!mDocument->CSSLoader()->HasPendingLoads()){mDocument->CSSLoader()->RemoveObserver(this);StartLayout(false);ScrollToRef();}returnNS_OK;}NS_IMETHODIMPnsXMLContentSink::WillInterrupt(void){returnWillInterruptImpl();}NS_IMETHODIMPnsXMLContentSink::WillResume(void){returnWillResumeImpl();}NS_IMETHODIMPnsXMLContentSink::SetParser(nsParserBase*aParser){NS_PRECONDITION(aParser,"Should have a parser here!");mParser=aParser;returnNS_OK;}nsresultnsXMLContentSink::CreateElement(constchar16_t**aAtts,uint32_taAttsCount,mozilla::dom::NodeInfo*aNodeInfo,uint32_taLineNumber,nsIContent**aResult,bool*aAppendContent,FromParseraFromParser){NS_ASSERTION(aNodeInfo,"can't create element without nodeinfo");*aResult=nullptr;*aAppendContent=true;nsresultrv=NS_OK;RefPtr<mozilla::dom::NodeInfo>ni=aNodeInfo;RefPtr<Element>content;rv=NS_NewElement(getter_AddRefs(content),ni.forget(),aFromParser);NS_ENSURE_SUCCESS(rv,rv);if(aNodeInfo->Equals(nsGkAtoms::script,kNameSpaceID_XHTML)||aNodeInfo->Equals(nsGkAtoms::script,kNameSpaceID_SVG)){nsCOMPtr<nsIScriptElement>sele=do_QueryInterface(content);if(sele){sele->SetScriptLineNumber(aLineNumber);sele->SetCreatorParser(GetParser());}else{MOZ_ASSERT(nsNameSpaceManager::GetInstance()->mSVGDisabled,"Node didn't QI to script, but SVG wasn't disabled.");}}// XHTML needs some special attentionif(aNodeInfo->NamespaceEquals(kNameSpaceID_XHTML)){mPrettyPrintHasFactoredElements=true;}else{// If we care, find out if we just used a special factory.if(!mPrettyPrintHasFactoredElements&&!mPrettyPrintHasSpecialRoot&&mPrettyPrintXML){mPrettyPrintHasFactoredElements=nsContentUtils::NameSpaceManager()->HasElementCreator(aNodeInfo->NamespaceID());}if(!aNodeInfo->NamespaceEquals(kNameSpaceID_SVG)){content.forget(aResult);returnNS_OK;}}if(aNodeInfo->Equals(nsGkAtoms::link,kNameSpaceID_XHTML)||aNodeInfo->Equals(nsGkAtoms::style,kNameSpaceID_XHTML)||aNodeInfo->Equals(nsGkAtoms::style,kNameSpaceID_SVG)){nsCOMPtr<nsIStyleSheetLinkingElement>ssle(do_QueryInterface(content));if(ssle){ssle->InitStyleLinkElement(false);if(aFromParser){ssle->SetEnableUpdates(false);}if(!aNodeInfo->Equals(nsGkAtoms::link,kNameSpaceID_XHTML)){ssle->SetLineNumber(aFromParser?aLineNumber:0);}}}content.forget(aResult);returnNS_OK;}nsresultnsXMLContentSink::CloseElement(nsIContent*aContent){NS_ASSERTION(aContent,"missing element to close");mozilla::dom::NodeInfo*nodeInfo=aContent->NodeInfo();// Some HTML nodes need DoneAddingChildren() called to initialize// properly (eg form state restoration).if((nodeInfo->NamespaceID()==kNameSpaceID_XHTML&&(nodeInfo->NameAtom()==nsGkAtoms::select||nodeInfo->NameAtom()==nsGkAtoms::textarea||nodeInfo->NameAtom()==nsGkAtoms::video||nodeInfo->NameAtom()==nsGkAtoms::audio||nodeInfo->NameAtom()==nsGkAtoms::object||nodeInfo->NameAtom()==nsGkAtoms::applet))||nodeInfo->NameAtom()==nsGkAtoms::title){aContent->DoneAddingChildren(HaveNotifiedForCurrentContent());}if(IsMonolithicContainer(nodeInfo)){mInMonolithicContainer--;}if(!nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)&&!nodeInfo->NamespaceEquals(kNameSpaceID_SVG)){returnNS_OK;}if(nodeInfo->Equals(nsGkAtoms::script,kNameSpaceID_XHTML)||nodeInfo->Equals(nsGkAtoms::script,kNameSpaceID_SVG)){nsCOMPtr<nsIScriptElement>sele=do_QueryInterface(aContent);if(!sele){MOZ_ASSERT(nsNameSpaceManager::GetInstance()->mSVGDisabled,"Node didn't QI to script, but SVG wasn't disabled.");returnNS_OK;}if(mPreventScriptExecution){sele->PreventExecution();returnNS_OK;}// Always check the clock in nsContentSink right after a scriptStopDeflecting();// Now tell the script that it's ready to go. This may execute the script// or return true, or neither if the script doesn't need executing.boolblock=sele->AttemptToExecute();// If the parser got blocked, make sure to return the appropriate rv.// I'm not sure if this is actually needed or not.if(mParser&&!mParser->IsParserEnabled()){block=true;}returnblock?NS_ERROR_HTMLPARSER_BLOCK:NS_OK;}nsresultrv=NS_OK;if(nodeInfo->Equals(nsGkAtoms::meta,kNameSpaceID_XHTML)&&// Need to check here to make sure this meta tag does not set// mPrettyPrintXML to false when we have a special root!(!mPrettyPrintXML||!mPrettyPrintHasSpecialRoot)){rv=ProcessMETATag(aContent);}elseif(nodeInfo->Equals(nsGkAtoms::link,kNameSpaceID_XHTML)||nodeInfo->Equals(nsGkAtoms::style,kNameSpaceID_XHTML)||nodeInfo->Equals(nsGkAtoms::style,kNameSpaceID_SVG)){nsCOMPtr<nsIStyleSheetLinkingElement>ssle(do_QueryInterface(aContent));if(ssle){ssle->SetEnableUpdates(true);boolwillNotify;boolisAlternate;rv=ssle->UpdateStyleSheet(mRunsToCompletion?nullptr:this,&willNotify,&isAlternate);if(NS_SUCCEEDED(rv)&&willNotify&&!isAlternate&&!mRunsToCompletion){++mPendingSheetCount;mScriptLoader->AddParserBlockingScriptExecutionBlocker();}}}returnrv;}nsresultnsXMLContentSink::AddContentAsLeaf(nsIContent*aContent){nsresultresult=NS_OK;if((eXMLContentSinkState_InProlog==mState)||(eXMLContentSinkState_InEpilog==mState)){NS_ASSERTION(mDocument,"Fragments have no prolog or epilog");mDocument->AppendChildTo(aContent,false);}else{nsCOMPtr<nsIContent>parent=GetCurrentContent();if(parent){result=parent->AppendChildTo(aContent,false);}}returnresult;}// Create an XML parser and an XSL content sink and start parsing// the XSL stylesheet located at the given URI.nsresultnsXMLContentSink::LoadXSLStyleSheet(nsIURI*aUrl){nsCOMPtr<nsIDocumentTransformer>processor=do_CreateInstance("@mozilla.org/document-transformer;1?type=xslt");if(!processor){// No XSLT processor available, continue normal document loadingreturnNS_OK;}processor->SetTransformObserver(this);if(NS_SUCCEEDED(processor->LoadStyleSheet(aUrl,mDocument))){mXSLTProcessor.swap(processor);}// Intentionally ignore errors here, we should continue loading the// XML document whether we're able to load the XSLT stylesheet or// not.returnNS_OK;}nsresultnsXMLContentSink::ProcessStyleLink(nsIContent*aElement,constnsAString&aHref,boolaAlternate,constnsAString&aTitle,constnsAString&aType,constnsAString&aMedia){nsresultrv=NS_OK;mPrettyPrintXML=false;nsAutoCStringcmd;if(mParser)GetParser()->GetCommand(cmd);if(cmd.EqualsASCII(kLoadAsData))returnNS_OK;// Do not load stylesheets when loading as dataNS_ConvertUTF16toUTF8type(aType);if(type.EqualsIgnoreCase(TEXT_XSL)||type.EqualsIgnoreCase(APPLICATION_XSLT_XML)||type.EqualsIgnoreCase(TEXT_XML)||type.EqualsIgnoreCase(APPLICATION_XML)){if(aAlternate){// don't load alternate XSLTreturnNS_OK;}// LoadXSLStyleSheet needs a mDocShell.if(!mDocShell)returnNS_OK;nsCOMPtr<nsIURI>url;rv=NS_NewURI(getter_AddRefs(url),aHref,nullptr,mDocument->GetDocBaseURI());NS_ENSURE_SUCCESS(rv,rv);// Do security checknsIScriptSecurityManager*secMan=nsContentUtils::GetSecurityManager();rv=secMan->CheckLoadURIWithPrincipal(mDocument->NodePrincipal(),url,nsIScriptSecurityManager::ALLOW_CHROME);NS_ENSURE_SUCCESS(rv,NS_OK);// Do content policy checkint16_tdecision=nsIContentPolicy::ACCEPT;rv=NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_XSLT,url,mDocument->NodePrincipal(),aElement,type,nullptr,&decision,nsContentUtils::GetContentPolicy(),nsContentUtils::GetSecurityManager());NS_ENSURE_SUCCESS(rv,rv);if(NS_CP_REJECTED(decision)){returnNS_OK;}returnLoadXSLStyleSheet(url);}// Let nsContentSink deal with css.rv=nsContentSink::ProcessStyleLink(aElement,aHref,aAlternate,aTitle,aType,aMedia);// nsContentSink::ProcessStyleLink handles the bookkeeping here wrt// pending sheets.returnrv;}voidnsXMLContentSink::SetDocumentCharset(NotNull<constEncoding*>aEncoding){if(mDocument){mDocument->SetDocumentCharacterSet(aEncoding);}}nsISupports*nsXMLContentSink::GetTarget(){returnmDocument;}boolnsXMLContentSink::IsScriptExecuting(){returnIsScriptExecutingImpl();}nsresultnsXMLContentSink::FlushText(boolaReleaseTextNode){nsresultrv=NS_OK;if(mTextLength!=0){if(mLastTextNode){boolnotify=HaveNotifiedForCurrentContent();// We could probably always increase mInNotification here since// if AppendText doesn't notify it shouldn't trigger evil code.// But just in case it does, we don't want to mask any notifications.if(notify){++mInNotification;}rv=mLastTextNode->AppendText(mText,mTextLength,notify);if(notify){--mInNotification;}mTextLength=0;}else{RefPtr<nsTextNode>textContent=newnsTextNode(mNodeInfoManager);mLastTextNode=textContent;// Set the text in the text nodetextContent->SetText(mText,mTextLength,false);mTextLength=0;// Add text to its parentrv=AddContentAsLeaf(textContent);}}if(aReleaseTextNode){mLastTextNode=nullptr;}returnrv;}nsIContent*nsXMLContentSink::GetCurrentContent(){if(mContentStack.Length()==0){returnnullptr;}returnGetCurrentStackNode()->mContent;}StackNode*nsXMLContentSink::GetCurrentStackNode(){int32_tcount=mContentStack.Length();returncount!=0?&mContentStack[count-1]:nullptr;}nsresultnsXMLContentSink::PushContent(nsIContent*aContent){NS_PRECONDITION(aContent,"Null content being pushed!");StackNode*sn=mContentStack.AppendElement();NS_ENSURE_TRUE(sn,NS_ERROR_OUT_OF_MEMORY);nsIContent*contentToPush=aContent;// When an XML parser would append a node to a template element, it// must instead append it to the template element's template contents.if(contentToPush->IsHTMLElement(nsGkAtoms::_template)){HTMLTemplateElement*templateElement=static_cast<HTMLTemplateElement*>(contentToPush);contentToPush=templateElement->Content();}sn->mContent=contentToPush;sn->mNumFlushed=0;returnNS_OK;}voidnsXMLContentSink::PopContent(){int32_tcount=mContentStack.Length();if(count==0){NS_WARNING("Popping empty stack");return;}mContentStack.RemoveElementAt(count-1);}boolnsXMLContentSink::HaveNotifiedForCurrentContent()const{uint32_tstackLength=mContentStack.Length();if(stackLength){constStackNode&stackNode=mContentStack[stackLength-1];nsIContent*parent=stackNode.mContent;returnstackNode.mNumFlushed==parent->GetChildCount();}returntrue;}voidnsXMLContentSink::MaybeStartLayout(boolaIgnorePendingSheets){// XXXbz if aIgnorePendingSheets is true, what should we do when// mXSLTProcessor or CanStillPrettyPrint()?if(mLayoutStarted||mXSLTProcessor||CanStillPrettyPrint()){return;}StartLayout(aIgnorePendingSheets);}////////////////////////////////////////////////////////////////////////boolnsXMLContentSink::SetDocElement(int32_taNameSpaceID,nsIAtom*aTagName,nsIContent*aContent){if(mDocElement)returnfalse;// check for root elements that needs special handling for// prettyprintingif((aNameSpaceID==kNameSpaceID_XBL&&aTagName==nsGkAtoms::bindings)||(aNameSpaceID==kNameSpaceID_XSLT&&(aTagName==nsGkAtoms::stylesheet||aTagName==nsGkAtoms::transform))){mPrettyPrintHasSpecialRoot=true;if(mPrettyPrintXML){// In this case, disable script execution, stylesheet// loading, and auto XLinks since we plan to prettyprint.mDocument->ScriptLoader()->SetEnabled(false);if(mCSSLoader){mCSSLoader->SetEnabled(false);}}}mDocElement=aContent;nsresultrv=mDocument->AppendChildTo(mDocElement,NotifyForDocElement());if(NS_FAILED(rv)){// If we return false here, the caller will bail out because it won't// find a parent content node to append to, which is fine.returnfalse;}if(aTagName==nsGkAtoms::html&&aNameSpaceID==kNameSpaceID_XHTML){ProcessOfflineManifest(aContent);}returntrue;}NS_IMETHODIMPnsXMLContentSink::HandleStartElement(constchar16_t*aName,constchar16_t**aAtts,uint32_taAttsCount,uint32_taLineNumber){returnHandleStartElement(aName,aAtts,aAttsCount,aLineNumber,true);}nsresultnsXMLContentSink::HandleStartElement(constchar16_t*aName,constchar16_t**aAtts,uint32_taAttsCount,uint32_taLineNumber,boolaInterruptable){NS_PRECONDITION(aAttsCount%2==0,"incorrect aAttsCount");// Adjust aAttsCount so it's the actual number of attributesaAttsCount/=2;nsresultresult=NS_OK;boolappendContent=true;nsCOMPtr<nsIContent>content;// XXX Hopefully the parser will flag this before we get// here. If we're in the epilog, there should be no// new elementsMOZ_ASSERT(eXMLContentSinkState_InEpilog!=mState);FlushText();DidAddContent();mState=eXMLContentSinkState_InDocumentElement;int32_tnameSpaceID;nsCOMPtr<nsIAtom>prefix,localName;nsContentUtils::SplitExpatName(aName,getter_AddRefs(prefix),getter_AddRefs(localName),&nameSpaceID);if(!OnOpenContainer(aAtts,aAttsCount,nameSpaceID,localName,aLineNumber)){returnNS_OK;}RefPtr<mozilla::dom::NodeInfo>nodeInfo;nodeInfo=mNodeInfoManager->GetNodeInfo(localName,prefix,nameSpaceID,nsIDOMNode::ELEMENT_NODE);result=CreateElement(aAtts,aAttsCount,nodeInfo,aLineNumber,getter_AddRefs(content),&appendContent,FROM_PARSER_NETWORK);NS_ENSURE_SUCCESS(result,result);// Have to do this before we push the new content on the stack... and have to// do that before we set attributes, call BindToTree, etc. Ideally we'd push// on the stack inside CreateElement (which is effectively what the HTML sink// does), but that's hard with all the subclass overrides going on.nsCOMPtr<nsIContent>parent=GetCurrentContent();result=PushContent(content);NS_ENSURE_SUCCESS(result,result);// Set the attributes on the new content elementresult=AddAttributes(aAtts,content);if(NS_OK==result){// Store the elementif(!SetDocElement(nameSpaceID,localName,content)&&appendContent){NS_ENSURE_TRUE(parent,NS_ERROR_UNEXPECTED);parent->AppendChildTo(content,false);}}// Some HTML nodes need DoneCreatingElement() called to initialize// properly (eg form state restoration).if(nodeInfo->NamespaceID()==kNameSpaceID_XHTML){if(nodeInfo->NameAtom()==nsGkAtoms::input||nodeInfo->NameAtom()==nsGkAtoms::button||nodeInfo->NameAtom()==nsGkAtoms::menuitem||nodeInfo->NameAtom()==nsGkAtoms::audio||nodeInfo->NameAtom()==nsGkAtoms::video){content->DoneCreatingElement();}elseif(nodeInfo->NameAtom()==nsGkAtoms::head&&!mCurrentHead){mCurrentHead=content;}}if(IsMonolithicContainer(nodeInfo)){mInMonolithicContainer++;}if(content!=mDocElement&&!mCurrentHead){// This isn't the root and we're not inside an XHTML <head>.// Might need to start layoutMaybeStartLayout(false);}if(content==mDocElement){NotifyDocElementCreated(mDocument);if(aInterruptable&&NS_SUCCEEDED(result)&&mParser&&!mParser->IsParserEnabled()){returnNS_ERROR_HTMLPARSER_BLOCK;}}returnaInterruptable&&NS_SUCCEEDED(result)?DidProcessATokenImpl():result;}NS_IMETHODIMPnsXMLContentSink::HandleEndElement(constchar16_t*aName){returnHandleEndElement(aName,true);}nsresultnsXMLContentSink::HandleEndElement(constchar16_t*aName,boolaInterruptable){nsresultresult=NS_OK;// XXX Hopefully the parser will flag this before we get// here. If we're in the prolog or epilog, there should be// no close tags for elements.MOZ_ASSERT(eXMLContentSinkState_InDocumentElement==mState);FlushText();StackNode*sn=GetCurrentStackNode();if(!sn){returnNS_ERROR_UNEXPECTED;}nsCOMPtr<nsIContent>content;sn->mContent.swap(content);uint32_tnumFlushed=sn->mNumFlushed;PopContent();NS_ASSERTION(content,"failed to pop content");#ifdef DEBUG// Check that we're closing the right thingnsCOMPtr<nsIAtom>debugNameSpacePrefix,debugTagAtom;int32_tdebugNameSpaceID;nsContentUtils::SplitExpatName(aName,getter_AddRefs(debugNameSpacePrefix),getter_AddRefs(debugTagAtom),&debugNameSpaceID);// Check if we are closing a template element because template// elements do not get pushed on the stack, the template// element content is pushed instead.boolisTemplateElement=debugTagAtom==nsGkAtoms::_template&&debugNameSpaceID==kNameSpaceID_XHTML;NS_ASSERTION(content->NodeInfo()->Equals(debugTagAtom,debugNameSpaceID)||(debugNameSpaceID==kNameSpaceID_MathML&&content->NodeInfo()->NamespaceID()==kNameSpaceID_disabled_MathML&&content->NodeInfo()->Equals(debugTagAtom))||(debugNameSpaceID==kNameSpaceID_SVG&&content->NodeInfo()->NamespaceID()==kNameSpaceID_disabled_SVG&&content->NodeInfo()->Equals(debugTagAtom))||isTemplateElement,"Wrong element being closed");#endifresult=CloseElement(content);if(mCurrentHead==content){mCurrentHead=nullptr;}if(mDocElement==content){// XXXbz for roots that don't want to be appended on open, we// probably need to deal here.... (and stop appending them on open).mState=eXMLContentSinkState_InEpilog;// We might have had no occasion to start layout yet. Do so now.MaybeStartLayout(false);}int32_tstackLen=mContentStack.Length();if(mNotifyLevel>=stackLen){if(numFlushed<content->GetChildCount()){NotifyAppend(content,numFlushed);}mNotifyLevel=stackLen-1;}DidAddContent();if(content->IsSVGElement(nsGkAtoms::svg)){FlushTags();nsCOMPtr<nsIRunnable>event=newnsHtml5SVGLoadDispatcher(content);if(NS_FAILED(content->OwnerDoc()->Dispatch("nsHtml5SVGLoadDispatcher",TaskCategory::Other,event.forget()))){NS_WARNING("failed to dispatch svg load dispatcher");}}returnaInterruptable&&NS_SUCCEEDED(result)?DidProcessATokenImpl():result;}NS_IMETHODIMPnsXMLContentSink::HandleComment(constchar16_t*aName){FlushText();RefPtr<Comment>comment=newComment(mNodeInfoManager);comment->SetText(nsDependentString(aName),false);nsresultrv=AddContentAsLeaf(comment);DidAddContent();returnNS_SUCCEEDED(rv)?DidProcessATokenImpl():rv;}NS_IMETHODIMPnsXMLContentSink::HandleCDataSection(constchar16_t*aData,uint32_taLength){// XSLT doesn't differentiate between text and cdata and wants adjacent// textnodes merged, so add as text.if(mXSLTProcessor){returnAddText(aData,aLength);}FlushText();RefPtr<CDATASection>cdata=newCDATASection(mNodeInfoManager);cdata->SetText(aData,aLength,false);nsresultrv=AddContentAsLeaf(cdata);DidAddContent();returnNS_SUCCEEDED(rv)?DidProcessATokenImpl():rv;}NS_IMETHODIMPnsXMLContentSink::HandleDoctypeDecl(constnsAString&aSubset,constnsAString&aName,constnsAString&aSystemId,constnsAString&aPublicId,nsISupports*aCatalogData){FlushText();nsresultrv=NS_OK;NS_ASSERTION(mDocument,"Shouldn't get here from a document fragment");nsCOMPtr<nsIAtom>name=NS_Atomize(aName);NS_ENSURE_TRUE(name,NS_ERROR_OUT_OF_MEMORY);// Create a new doctype nodensCOMPtr<nsIDOMDocumentType>docType;rv=NS_NewDOMDocumentType(getter_AddRefs(docType),mNodeInfoManager,name,aPublicId,aSystemId,aSubset);if(NS_FAILED(rv)||!docType){returnrv;}MOZ_ASSERT(!aCatalogData,"Need to add back support for catalog style ""sheets");nsCOMPtr<nsIContent>content=do_QueryInterface(docType);NS_ASSERTION(content,"doctype isn't content?");rv=mDocument->AppendChildTo(content,false);DidAddContent();returnNS_SUCCEEDED(rv)?DidProcessATokenImpl():rv;}NS_IMETHODIMPnsXMLContentSink::HandleCharacterData(constchar16_t*aData,uint32_taLength){returnHandleCharacterData(aData,aLength,true);}nsresultnsXMLContentSink::HandleCharacterData(constchar16_t*aData,uint32_taLength,boolaInterruptable){nsresultrv=NS_OK;if(aData&&mState!=eXMLContentSinkState_InProlog&&mState!=eXMLContentSinkState_InEpilog){rv=AddText(aData,aLength);}returnaInterruptable&&NS_SUCCEEDED(rv)?DidProcessATokenImpl():rv;}NS_IMETHODIMPnsXMLContentSink::HandleProcessingInstruction(constchar16_t*aTarget,constchar16_t*aData){FlushText();constnsDependentStringtarget(aTarget);constnsDependentStringdata(aData);nsCOMPtr<nsIContent>node=NS_NewXMLProcessingInstruction(mNodeInfoManager,target,data);nsCOMPtr<nsIStyleSheetLinkingElement>ssle(do_QueryInterface(node));if(ssle){ssle->InitStyleLinkElement(false);ssle->SetEnableUpdates(false);mPrettyPrintXML=false;}nsresultrv=AddContentAsLeaf(node);NS_ENSURE_SUCCESS(rv,rv);DidAddContent();if(ssle){// This is an xml-stylesheet processing instruction... but it might not be// a CSS one if the type is set to something else.ssle->SetEnableUpdates(true);boolwillNotify;boolisAlternate;rv=ssle->UpdateStyleSheet(mRunsToCompletion?nullptr:this,&willNotify,&isAlternate);NS_ENSURE_SUCCESS(rv,rv);if(willNotify){// Successfully started a stylesheet loadif(!isAlternate&&!mRunsToCompletion){++mPendingSheetCount;mScriptLoader->AddParserBlockingScriptExecutionBlocker();}returnNS_OK;}}// If it's not a CSS stylesheet PI...nsAutoStringtype;nsContentUtils::GetPseudoAttributeValue(data,nsGkAtoms::type,type);if(mState!=eXMLContentSinkState_InProlog||!target.EqualsLiteral("xml-stylesheet")||type.IsEmpty()||type.LowerCaseEqualsLiteral("text/css")){returnDidProcessATokenImpl();}nsAutoStringhref,title,media;boolisAlternate=false;// If there was no href, we can't do anything with this PIif(!ParsePIData(data,href,title,media,isAlternate)){returnDidProcessATokenImpl();}rv=ProcessStyleLink(node,href,isAlternate,title,type,media);returnNS_SUCCEEDED(rv)?DidProcessATokenImpl():rv;}/* static */boolnsXMLContentSink::ParsePIData(constnsString&aData,nsString&aHref,nsString&aTitle,nsString&aMedia,bool&aIsAlternate){// If there was no href, we can't do anything with this PIif(!nsContentUtils::GetPseudoAttributeValue(aData,nsGkAtoms::href,aHref)){returnfalse;}nsContentUtils::GetPseudoAttributeValue(aData,nsGkAtoms::title,aTitle);nsContentUtils::GetPseudoAttributeValue(aData,nsGkAtoms::media,aMedia);nsAutoStringalternate;nsContentUtils::GetPseudoAttributeValue(aData,nsGkAtoms::alternate,alternate);aIsAlternate=alternate.EqualsLiteral("yes");returntrue;}NS_IMETHODIMPnsXMLContentSink::HandleXMLDeclaration(constchar16_t*aVersion,constchar16_t*aEncoding,int32_taStandalone){mDocument->SetXMLDeclaration(aVersion,aEncoding,aStandalone);returnDidProcessATokenImpl();}NS_IMETHODIMPnsXMLContentSink::ReportError(constchar16_t*aErrorText,constchar16_t*aSourceText,nsIScriptError*aError,bool*_retval){NS_PRECONDITION(aError&&aSourceText&&aErrorText,"Check arguments!!!");nsresultrv=NS_OK;// The expat driver should report the error. We're just cleaning up the mess.*_retval=true;mPrettyPrintXML=false;mState=eXMLContentSinkState_InProlog;// XXX need to stop scripts here -- hsivonen// stop observing in order to avoid crashing when removing contentmDocument->RemoveObserver(this);mIsDocumentObserver=false;// Clear the current contentnsCOMPtr<nsIDOMNode>node(do_QueryInterface(mDocument));if(node){for(;;){nsCOMPtr<nsIDOMNode>child,dummy;node->GetLastChild(getter_AddRefs(child));if(!child)break;node->RemoveChild(child,getter_AddRefs(dummy));}}mDocElement=nullptr;// Clear any buffered-up text we have. It's enough to set the length to 0.// The buffer itself is allocated when we're created and deleted in our// destructor, so don't mess with it.mTextLength=0;if(mXSLTProcessor){// Get rid of the XSLT processor.mXSLTProcessor->CancelLoads();mXSLTProcessor=nullptr;}// release the nodes on stackmContentStack.Clear();mNotifyLevel=0;// return leaving the document empty if we're asked to not add a <parsererror> root nodeif(mDocument->SuppressParserErrorElement()){returnNS_OK;}// prepare to set <parsererror> as the document rootrv=HandleProcessingInstruction(u"xml-stylesheet",u"href=\"chrome://global/locale/intl.css\" type=\"text/css\"");NS_ENSURE_SUCCESS(rv,rv);constchar16_t*noAtts[]={0,0};NS_NAMED_LITERAL_STRING(errorNs,"http://www.mozilla.org/newlayout/xml/parsererror.xml");nsAutoStringparsererror(errorNs);parsererror.Append((char16_t)0xFFFF);parsererror.AppendLiteral("parsererror");rv=HandleStartElement(parsererror.get(),noAtts,0,(uint32_t)-1,false);NS_ENSURE_SUCCESS(rv,rv);rv=HandleCharacterData(aErrorText,NS_strlen(aErrorText),false);NS_ENSURE_SUCCESS(rv,rv);nsAutoStringsourcetext(errorNs);sourcetext.Append((char16_t)0xFFFF);sourcetext.AppendLiteral("sourcetext");rv=HandleStartElement(sourcetext.get(),noAtts,0,(uint32_t)-1,false);NS_ENSURE_SUCCESS(rv,rv);rv=HandleCharacterData(aSourceText,NS_strlen(aSourceText),false);NS_ENSURE_SUCCESS(rv,rv);rv=HandleEndElement(sourcetext.get(),false);NS_ENSURE_SUCCESS(rv,rv);rv=HandleEndElement(parsererror.get(),false);NS_ENSURE_SUCCESS(rv,rv);FlushTags();returnNS_OK;}nsresultnsXMLContentSink::AddAttributes(constchar16_t**aAtts,nsIContent*aContent){// Add tag attributes to the content attributesnsCOMPtr<nsIAtom>prefix,localName;while(*aAtts){int32_tnameSpaceID;nsContentUtils::SplitExpatName(aAtts[0],getter_AddRefs(prefix),getter_AddRefs(localName),&nameSpaceID);// Add attribute to contentaContent->SetAttr(nameSpaceID,localName,prefix,nsDependentString(aAtts[1]),false);aAtts+=2;}returnNS_OK;}#define NS_ACCUMULATION_BUFFER_SIZE 4096nsresultnsXMLContentSink::AddText(constchar16_t*aText,int32_taLength){// Copy data from string into our buffer; flush buffer when it fills up.int32_toffset=0;while(0!=aLength){int32_tamount=NS_ACCUMULATION_BUFFER_SIZE-mTextLength;if(0==amount){nsresultrv=FlushText(false);if(NS_WARN_IF(NS_FAILED(rv))){returnrv;}MOZ_ASSERT(mTextLength==0);amount=NS_ACCUMULATION_BUFFER_SIZE;}if(amount>aLength){amount=aLength;}memcpy(&mText[mTextLength],&aText[offset],sizeof(char16_t)*amount);mTextLength+=amount;offset+=amount;aLength-=amount;}returnNS_OK;}voidnsXMLContentSink::FlushPendingNotifications(FlushTypeaType){// Only flush tags if we're not doing the notification ourselves// (since we aren't reentrant)if(!mInNotification){if(mIsDocumentObserver){// Only flush if we're still a document observer (so that our child// counts should be correct).if(aType>=FlushType::ContentAndNotify){FlushTags();}else{FlushText(false);}}if(aType>=FlushType::InterruptibleLayout){// Make sure that layout has started so that the reflow flush// will actually happen.MaybeStartLayout(true);}}}/** * NOTE!! Forked from SinkContext. Please keep in sync. * * Flush all elements that have been seen so far such that * they are visible in the tree. Specifically, make sure * that they are all added to their respective parents. * Also, do notification at the top for all content that * has been newly added so that the frame tree is complete. */nsresultnsXMLContentSink::FlushTags(){mDeferredFlushTags=false;boololdBeganUpdate=mBeganUpdate;uint32_toldUpdates=mUpdatesInNotification;mUpdatesInNotification=0;++mInNotification;{// Scope so we call EndUpdate before we decrease mInNotificationmozAutoDocUpdateupdateBatch(mDocument,UPDATE_CONTENT_MODEL,true);mBeganUpdate=true;// Don't release last text node in case we need to add to it againFlushText(false);// Start from the base of the stack (growing downward) and do// a notification from the node that is closest to the root of// tree for any content that has been added.int32_tstackPos;int32_tstackLen=mContentStack.Length();boolflushed=false;uint32_tchildCount;nsIContent*content;for(stackPos=0;stackPos<stackLen;++stackPos){content=mContentStack[stackPos].mContent;childCount=content->GetChildCount();if(!flushed&&(mContentStack[stackPos].mNumFlushed<childCount)){NotifyAppend(content,mContentStack[stackPos].mNumFlushed);flushed=true;}mContentStack[stackPos].mNumFlushed=childCount;}mNotifyLevel=stackLen-1;}--mInNotification;if(mUpdatesInNotification>1){UpdateChildCounts();}mUpdatesInNotification=oldUpdates;mBeganUpdate=oldBeganUpdate;returnNS_OK;}/** * NOTE!! Forked from SinkContext. Please keep in sync. */voidnsXMLContentSink::UpdateChildCounts(){// Start from the top of the stack (growing upwards) and see if any// new content has been appended. If so, we recognize that reflows// have been generated for it and we should make sure that no// further reflows occur. Note that we have to include stackPos == 0// to properly notify on kids of <html>.int32_tstackLen=mContentStack.Length();int32_tstackPos=stackLen-1;while(stackPos>=0){StackNode&node=mContentStack[stackPos];node.mNumFlushed=node.mContent->GetChildCount();stackPos--;}mNotifyLevel=stackLen-1;}boolnsXMLContentSink::IsMonolithicContainer(mozilla::dom::NodeInfo*aNodeInfo){return((aNodeInfo->NamespaceID()==kNameSpaceID_XHTML&&(aNodeInfo->NameAtom()==nsGkAtoms::tr||aNodeInfo->NameAtom()==nsGkAtoms::select||aNodeInfo->NameAtom()==nsGkAtoms::object||aNodeInfo->NameAtom()==nsGkAtoms::applet))||(aNodeInfo->NamespaceID()==kNameSpaceID_MathML&&(aNodeInfo->NameAtom()==nsGkAtoms::math)));}voidnsXMLContentSink::ContinueInterruptedParsingIfEnabled(){if(mParser&&mParser->IsParserEnabled()){GetParser()->ContinueInterruptedParsing();}}voidnsXMLContentSink::ContinueInterruptedParsingAsync(){nsCOMPtr<nsIRunnable>ev=NewRunnableMethod("nsXMLContentSink::ContinueInterruptedParsingIfEnabled",this,&nsXMLContentSink::ContinueInterruptedParsingIfEnabled);NS_DispatchToCurrentThread(ev);}nsIParser*nsXMLContentSink::GetParser(){returnstatic_cast<nsIParser*>(mParser.get());}